{/* Copyright 2020 Adobe. All rights reserved.
This file is licensed to you under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. You may obtain a copy
of the License at http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
OF ANY KIND, either express or implied. See the License for the specific language
governing permissions and limitations under the License. */}

import {Layout} from '@react-spectrum/docs';
export default Layout;

import DialogAnatomy from './images/DialogAnatomy.svg';
import docs from 'docs:@react-spectrum/dialog';
import {Image, HeaderInfo, PropTable, PageDescription, VersionBadge, ClassAPI} from '@react-spectrum/docs';
import packageData from '@react-spectrum/dialog/package.json';
import styles from '@react-spectrum/docs/src/docs.css';

```jsx import
import Book from '@spectrum-icons/workflow/Book';
import {Footer} from '@react-spectrum/view';
import {Checkbox} from '@react-spectrum/checkbox';
import {Flex} from '@react-spectrum/layout';
import {Form} from '@react-spectrum/form';
import {Link} from '@react-spectrum/link';
import {Image} from '@react-spectrum/image';
import {TextField} from '@react-spectrum/textfield';
```

---
category: Overlays
keywords: [modal, tray, popover]
---

# Dialog

<PageDescription>{docs.exports.Dialog.description}</PageDescription>

<HeaderInfo
  packageData={packageData}
  componentNames={['Dialog', 'DialogTrigger']}
  sourceData={[
    {type: 'Spectrum', url: 'https://spectrum.adobe.com/page/alert-dialog/'}
  ]}
  since="3.0.0" />

## Example

```tsx example
import {ActionButton, Button} from '@react-spectrum/button';
import {ButtonGroup} from '@react-spectrum/buttongroup';
import {Content, Header} from '@react-spectrum/view';
import {Dialog, DialogTrigger} from '@react-spectrum/dialog';
import {Divider} from '@react-spectrum/divider';
import {Heading, Text} from '@react-spectrum/text';

<DialogTrigger>
  <ActionButton>Check connectivity</ActionButton>
  {(close) => (
    <Dialog>
      <Heading>Internet Speed Test</Heading>
      <Header>Connection status: Connected</Header>
      <Divider />
      <Content>
        <Text>
          Start speed test?
        </Text>
      </Content>
      <ButtonGroup>
        <Button variant="secondary" onPress={close}>Cancel</Button>
        <Button variant="accent" onPress={close}>Confirm</Button>
      </ButtonGroup>
    </Dialog>
  )}
</DialogTrigger>
```

## Content

A standard Dialog has the following anatomy:

<div className={styles.exampleImage}>
  <DialogAnatomy />
</div>

These sections can be populated by providing the following components to your Dialog as children:
[Header](Header.html), [Heading](Heading.html) (title), [Divider](Divider.html), [Content](Content.html) (body),
[ButtonGroup](ButtonGroup.html), and [Footer](Footer.html). Each of these components are
required in a Spectrum compliant Dialog except for `Header`, `Footer`, and `Divider` so be sure to design your Dialog accordingly.

### Examples
A typical Dialog with a title, contents, and action buttons can be created like so:

```tsx example
<DialogTrigger>
  <ActionButton>Publish</ActionButton>
  {(close) => (
    <Dialog>
      <Heading>Publish 3 pages</Heading>
      <Divider />
      <Content>Confirm publish?</Content>
      <ButtonGroup>
        <Button variant="secondary" onPress={close}>Cancel</Button>
        <Button variant="accent" onPress={close} autoFocus>Confirm</Button>
      </ButtonGroup>
    </Dialog>
  )}
</DialogTrigger>
```

A dismissable Dialog forgoes its ButtonGroup in favor of rendering a close button at the top right of the Dialog.

```tsx example
<DialogTrigger isDismissable>
  <ActionButton>Status</ActionButton>
  <Dialog>
    <Heading>Status</Heading>
    <Divider />
    <Content>Printer Status: Connected</Content>
  </Dialog>
</DialogTrigger>
```

It is important to note that the `Heading`, `Header`, `Content`, and `Footer` content elements accept any renderable
node, not just strings. This allows you to create Dialogs for more complex workflows, such as including a form
for the user to fill out or adding confirmation checkboxes.

```tsx example
<DialogTrigger>
  <ActionButton>Register</ActionButton>
  {(close) => (
    <Dialog>
      <Heading>
        <Flex alignItems="center" gap="size-100">
          <Book size="S" />
          <Text>
            Register for newsletter
          </Text>
        </Flex>
      </Heading>
      <Header>
        <Link>
          <a href="//example.com" target="_blank">What is this?</a>
        </Link>
      </Header>
      <Divider />
      <Content>
        <Form>
          <TextField label="First Name" autoFocus />
          <TextField label="Last Name" />
          <TextField label="Street Address" />
          <TextField label="City" />
        </Form>
      </Content>
      <Footer>
        <Checkbox>
          I want to receive updates for exclusive offers in my area.
        </Checkbox>
      </Footer>
      <ButtonGroup>
        <Button variant="secondary" onPress={close}>Cancel</Button>
        <Button variant="accent" onPress={close}>Register</Button>
      </ButtonGroup>
    </Dialog>
  )}
</DialogTrigger>
```

The example below illustrates how a Dialog with a hero image could be rendered via the `hero` slot:

```tsx example
<DialogTrigger>
  <ActionButton>Upload</ActionButton>
  {(close) => (
    <Dialog>
      <Image slot="hero" alt="" src="https://i.imgur.com/Z7AzH2c.png" objectFit="cover" />
      <Heading>Upload file</Heading>
      <Divider />
      <Content>Are you sure you want to upload this file?</Content>
      <ButtonGroup>
        <Button variant="secondary" onPress={close}>Cancel</Button>
        <Button variant="accent" onPress={close} autoFocus>Confirm</Button>
      </ButtonGroup>
    </Dialog>
  )}
</DialogTrigger>
```

### Accessibility

Keep in mind when creating your Dialog that the tab order within the Dialog follows the order of the children provided.
You are also responsible for determining what component, if any, should be automatically focused when the Dialog opens.

## Labeling

### Accessibility

The title of a Dialog is typically provided via its heading. By default, the Dialog sets its
`aria-labelledby` to match the heading `id`, but this can be overridden by providing an `aria-labelledby` prop
to the Dialog directly. If a visible label isn't specified, an `aria-label` must be provided instead.

## Events

For Dialogs, user defined callbacks should be chained with the DialogTrigger's `close` function in the `onPress` handler
of the Dialog's action buttons. The following example alerts if the Dialog's save or cancel button is clicked.

```tsx example
function Example() {
  let alertSave = (close) => {
    close();
    alert('Profile saved!');
  }

  let alertCancel = (close) => {
    close();
    alert('Profile not saved!');
  }

  return (
    <DialogTrigger>
      <ActionButton>Set Profile</ActionButton>
      {(close) => (
        <Dialog>
          <Heading>Profile</Heading>
          <Divider />
          <ButtonGroup>
            <Button variant="secondary" onPress={() => alertCancel(close)}>Cancel</Button>
            <Button autoFocus variant="accent" onPress={() => alertSave(close)}>Save</Button>
          </ButtonGroup>
          <Content>
            <Form>
              <TextField label="Name" />
              <Checkbox>Make private</Checkbox>
            </Form>
          </Content>
        </Dialog>
      )}
    </DialogTrigger>
  );
}
```

Additionally, DialogTrigger accepts an `onOpenChange` prop which is triggered whenever the Dialog is opened or closed.
For more information, see the [DialogTrigger docs](./DialogTrigger.html#events).

### Dismissable dialogs

Dismissable Dialogs support an optional `onDismiss` prop which is triggered whenever the Dialog's close button is clicked.
Similar to non-dismissable dialogs, you must chain the DialogTrigger's `close` function with whatever callback you provide as
`onDismiss`. If this event callback is not needed, the dismissiable dialog will behave as normal without passing this callback through.

```tsx example
function Example() {
  let alertDismiss = (close) => {
    close();
    alert('Dialog dismissed.');
  }
  return (
    <DialogTrigger isDismissable>
      <ActionButton>Info</ActionButton>
      {(close) => (
        <Dialog onDismiss={() => alertDismiss(close)}>
          <Heading>Version Info</Heading>
          <Divider />
          <Content>
            <Text>
              Version 1.0.0, Copyright 2020
            </Text>
          </Content>
        </Dialog>
        )}
    </DialogTrigger>
  );
}
```

**Note:** The `onDismiss` callback is optional. If you don't need to add a `onDismiss` handler to your dismissable Dialog, you may omit
the wrapping `close` function surrounding the Dialog. An example of this can be found in the Examples section [above](#examples).

## Props

<PropTable component={docs.exports.Dialog} links={docs.links} />

## Visual options

### Dialog types
Dialogs can be rendered as modals, popovers, or trays. Note that popovers are displayed as modals by default on mobile.
See the [DialogTrigger docs](./DialogTrigger.html#dialog-types) for more information.

```tsx example
<DialogTrigger isDismissable type="modal">
  <ActionButton>Trigger Modal</ActionButton>
  <Dialog>
    <Heading>Modal</Heading>
    <Divider />
    <Content>
      <Text>
        This is a modal.
      </Text>
    </Content>
  </Dialog>
</DialogTrigger>
```
```tsx example
<DialogTrigger type="popover">
  <ActionButton>Trigger Popover</ActionButton>
  <Dialog>
    <Heading>Popover</Heading>
    <Divider />
    <Content>
      <Text>
        This is a popover.
      </Text>
    </Content>
  </Dialog>
</DialogTrigger>
```
```tsx example
<DialogTrigger type="tray">
  <ActionButton>Trigger Tray</ActionButton>
  <Dialog>
    <Heading>Tray</Heading>
    <Divider />
    <Content>
      <Text>
        This is a tray.
      </Text>
    </Content>
  </Dialog>
</DialogTrigger>
```

### Size
Only `modal` type Dialogs support a user defined size prop. Note that the `fullscreen` and `fullscreenTakeover` sizes
require the [DialogTrigger `type`](./DialogTrigger.html#dialog-types) prop to be set to `fullscreen` or `fullscreenTakeover`
respectively for container sizing considerations. Modal sizes on mobile devices are also unaffected by this prop due to screen constraints.

```tsx example
<DialogTrigger>
  <ActionButton>Small</ActionButton>
  {(close) => (
    <Dialog size="S">
      <Heading>Profile</Heading>
      <Divider />
      <ButtonGroup>
        <Button variant="secondary" onPress={close}>Cancel</Button>
        <Button autoFocus variant="accent" onPress={close}>Save</Button>
      </ButtonGroup>
      <Content>
        <Form>
          <TextField label="Name" />
          <Checkbox>Make private</Checkbox>
        </Form>
      </Content>
    </Dialog>
  )}
</DialogTrigger>
```
```tsx example
<DialogTrigger>
  <ActionButton>Medium</ActionButton>
  {(close) => (
    <Dialog size="M">
      <Heading>Profile</Heading>
      <Divider />
      <ButtonGroup>
        <Button variant="secondary" onPress={close}>Cancel</Button>
        <Button autoFocus variant="accent" onPress={close}>Save</Button>
      </ButtonGroup>
      <Content>
        <Form>
          <TextField label="Name" />
          <Checkbox>Make private</Checkbox>
        </Form>
      </Content>
    </Dialog>
  )}
</DialogTrigger>
```
```tsx example
<DialogTrigger>
  <ActionButton>Large</ActionButton>
  {(close) => (
    <Dialog size="L">
      <Heading>Profile</Heading>
      <Divider />
      <ButtonGroup>
        <Button variant="secondary" onPress={close}>Cancel</Button>
        <Button autoFocus variant="accent" onPress={close}>Save</Button>
      </ButtonGroup>
      <Content>
        <Form>
          <TextField label="Name" />
          <Checkbox>Make private</Checkbox>
        </Form>
      </Content>
    </Dialog>
  )}
</DialogTrigger>
```
